package com.charles.seckillGateway.filter;

import com.charles.seckillComponentCommon.constants.CacheConstants;
import com.charles.seckillComponentCommon.constants.RoleConstants;
import com.charles.seckillComponentCommon.constants.TokenConstants;
import com.charles.seckillComponentCommon.utils.JwtUtils;
import com.charles.seckillComponentCommon.utils.WebFluxUtil;
import com.charles.seckillComponentRedis.service.RedisService;
import com.charles.seckillGateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @author Charles-H
 * 
 * 对认证进行过滤
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    
    public static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter.class);
    
    @Autowired
    private IgnoreWhiteProperties ignoreWhiteProperties;
    
    @Autowired
    private RedisService redisService;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        String path = request.getURI().getPath();

        // 判断路径是否为白名单
        for (String pattern : ignoreWhiteProperties.getWhiteList()) {
            AntPathMatcher matcher = new AntPathMatcher();
            if (matcher.match(pattern, path)) {
                return chain.filter(exchange);
            }
        }
        
        String token = getToken(request);
        if (token.isEmpty()) return unauthorizedResponse(exchange, "令牌为空");

        Claims claims = JwtUtils.parseToken(token);
        if (claims == null) return unauthorizedResponse(exchange, "令牌已过期或验证失败");
        
        // 获取客户信息
        String userKey = JwtUtils.getUserKey(token);
        Boolean isLogin = redisService.hasKey(getTokenVerifyKey(userKey));
        if (!isLogin) return unauthorizedResponse(exchange, "登录已过期，请重新登陆");

        String userId = JwtUtils.getUserId(claims);
        String userName = JwtUtils.getUserName(claims);
        String userCard = JwtUtils.getUserCard(claims);
        if (userId.isEmpty() || userCard.isEmpty() || userName.isEmpty()) return unauthorizedResponse(exchange, "令牌验证失败");

        /*
            设置客户信息到请求头
         */
        addHeader(mutate, RoleConstants.USER_KYE, userKey);
        addHeader(mutate, RoleConstants.USER_ID, userId);
        addHeader(mutate, RoleConstants.USERNAME, userName);
        addHeader(mutate, RoleConstants.USER_CARD, userCard);
        
        /*
            清除请求头
         */
        removeHeader(mutate, RoleConstants.FROM_SOURCE);
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    /**
     * 获取Token令牌
     */
    public static String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(TokenConstants.TOKEN_AUTHENTICATION);
        
        // startsWith检测是否含前缀
        if (!token.isEmpty() && token.startsWith(TokenConstants.TOKEN_PREFIX)) {
            token = token.replaceFirst(TokenConstants.TOKEN_PREFIX, "");
        }
        return token;
    }
    
    /**
     * 获取Token令牌标识
     */
    private String getTokenVerifyKey(String token) {
        return CacheConstants.CACHE_TOKEN + token;
    }
    
    /**
     * 添加请求头
     */
    private void addHeader(ServerHttpRequest.Builder mutate, String title, Object obj) {
        if (obj == null) return;

        String value = obj.toString();
        // 加密
        String valueEncode = null;
        try {
            valueEncode = URLEncoder.encode(value, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            valueEncode = "";
        }
        mutate.header(title, valueEncode);
    }
    
    /**
     * 移除请求头
     */
    private void removeHeader(ServerHttpRequest.Builder mutate, String title) {
        mutate.headers(httpHeaders -> httpHeaders.remove(title)).build();
    }
    
    /**
     * 异常处理
     */
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        LOGGER.error("[认证异常]请求路径：{}", exchange.getRequest().getPath());
        return WebFluxUtil.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED.value());
    }
    
    @Override
    public int getOrder() {
        return -200;
    }
}
